--[[
	Copyright (C) Udorn (Blackhand)
--]]

lootlog = lootlog or {}

lootlog.LootWindow = {}
lootlog.LootWindow.prototype = {}
lootlog.LootWindow.metatable = {__index = lootlog.LootWindow.prototype}

local log = lootlog.Debug:new("LootWindow")

local WAIT_PATTERN = "|TInterface\\Archeology\\Arch-Icon-Marker:18|t %s"
local DISENCHANT_PATTERN = "%s |TInterface\\BUTTONS\\UI-GroupLoot-DE-Up:18|t %s"
local NEED_PATTERN = "%s |TInterface\\BUTTONS\\UI-GroupLoot-Dice-Up:18|t %s"
local GREED_PATTERN = "%s |TInterface\\BUTTONS\\UI-GroupLoot-Coin-Up:18|t %s"
local PASSED_PATTERN = "%s |TInterface\\BUTTONS\\UI-GroupLoot-Pass-Up:18|t"

lootlog.LootWindow.NEED = 1
lootlog.LootWindow.GREED = 2
lootlog.LootWindow.DISENCHANT = 3
lootlog.LootWindow.PASSED = 4

local NEED = lootlog.LootWindow.NEED
local GREED = lootlog.LootWindow.GREED
local DISENCHANT = lootlog.LootWindow.DISENCHANT
local PASSED = lootlog.LootWindow.PASSED

local UPDATE_DELAY = 0.4
local NUM_ENTRIES = 10
local WIDTH = 200
local WIDTH_OFFSET = 60

local function _GetFontColorCode(color)
   local r = math.floor(color.r * 255.0);
   local g = math.floor(color.g * 255.0);
   local b = math.floor(color.b * 255.0);
   return string.format("|cff%2x%2x%2x", r, g, b);
end

local function _DetermineIntermediate(self, item)
	item.needName = nil
	item.numNeed = 0
	for name, roll in pairs(item.rolls) do
		if (roll.roll == NEED) then
			item.needName = name
			item.numNeed = item.numNeed + 1
		end
	end
end

local function _RestoreWindow(self)
	local save = self.db.profile.windowPosition
	if (save) then
		if (save.point) then
			self.frame:ClearAllPoints()
			self.frame:SetPoint(save.point, UIParent, save.relativePoint, save.x, save.y)
		end
	end
end

local function _InitFrame(self)
	local frame = CreateFrame("Frame", "LootMaster", UIParent)
	frame.obj = self
	-- size doesn't matter
	frame:SetWidth(1)
	frame:SetHeight(1)
	frame:SetMovable(true)
	frame:SetScript("OnUpdate", function(this, delta) this.obj:Update(delta); end)
--	local texture = frame:CreateTexture()
--	texture:SetAllPoints(frame)
--	texture:SetTexture(1, 1, 1, 1)
	frame:SetPoint("CENTER")
	frame:Hide()

	self.entries = {}
	for i=1,NUM_ENTRIES do
		local entry = lootlog.LootEntry:new(frame, i)
		if (i == 1) then
			entry:SetPoint("TOPLEFT")
		else
			entry:SetPoint("TOPLEFT", self.entries[i - 1].frame, "BOTTOMLEFT", 0, 0)
		end
		self.entries[i] = entry
	end
	
	self.frame = frame
end

local function _GetPattern(selection)
	local pattern = NEED_PATTERN
	if (selection == lootlog.LootWindow.GREED) then
		pattern = GREED_PATTERN
	elseif (selection == lootlog.LootWindow.DISENCHANT) then
		pattern = DISENCHANT_PATTERN
	end
	return pattern
end

local function _UpdateUnits(self, prefix, n)
	if (n and type(n) == "number" and n > 0) then
		for i=1,n do
			local target = prefix..i
			local name = UnitName(target)
			local class, englishClass = UnitClass(target)
			if (name and class) then
				self.classCache[name] = class
				self.englishClassCache[name] = englishClass
			end
		end
	end
end

local function _InitClassCache(self)
	wipe(self.classCache)
	wipe(self.englishClassCache)
	if (self.db.profile.testMode) then
		self.classCache["Lethan"] = "Death Knight"
		self.classCache["Borkan"] = "Hunter"
		self.englishClassCache["Lethan"] = "DEATHKNIGHT"
		self.englishClassCache["Borkan"] = "HUNTER"
	end
	local n = UnitInRaid("player")
	if (n) then
		_UpdateUnits(self, "raid", n)
	end
	local n = UnitInParty("player")
	if (n) then
		_UpdateUnits(self, "party", n)
	end
	local class, englishClass = UnitClass("player")
	self.classCache[self.playerName] = class
	self.englishClassCache[self.playerName] = englishClass
end

local function _GetName(self, name)
	local suffix = ""
	local class = self.classCache[name]
	local englishClass = self.englishClassCache[name]
	if (self.db.profile.showClass and name ~= self.playerName and not class) then
		_InitClassCache(self)
		class = self.classCache[name]
	end
	if (self.db.profile.colorizePlayerName and not englishClass) then
		_InitClassCache(self)
		englishClass = self.englishClassCache[name]
	end
	if (self.db.profile.showClass and class) then
		suffix = " ("..class..")"
	end
	if (self.db.profile.colorizePlayerName and englishClass) then
		local color = RAID_CLASS_COLORS[englishClass]
		if (color) then
			name = _GetFontColorCode(color)..name.."|r"
		end
	end 
	return name..suffix
end

local function _UpdateItems(self)
	local n = getn(self.items)
	local maxWidth = 0
	for i=1,NUM_ENTRIES do
		local entry = self.entries[i]
		local item = self.items[i]
		if (i > n) then
			entry:Hide()
		else
			entry:Show()
			entry:SetBackgroundColor(unpack(self.db.profile.backgroundColor))
			entry:SetBorderColor(GetItemQualityColor(item.quality))
			entry:SetItem(item.itemLink)
			
			if (not item.finishedAt) then
				local msec = item.testMsec or GetLootRollTimeLeft(item.rollId or 0)
				
				local prefix = ""
				if (item.needName) then
					if (item.numNeed == 1) then
						prefix = string.format(_GetPattern(NEED), _GetName(self, item.needName), "")
					else
						prefix = string.format(_GetPattern(NEED), item.numNeed, "")
					end
				end
				
				if (msec and msec > 0 and msec < 300000) then
					local width
					if (strlen(prefix) > 0) then
						prefix = prefix.."- "
					end
					if (strlen(prefix) > 0) then
						width = entry:SetWinnerText(prefix..SecondsToTime(msec / 1000))
					else
						width = entry:SetWinnerText(prefix..string.format(WAIT_PATTERN, SecondsToTime(msec / 1000)))
					end
					if (width and width > maxWidth) then
						maxWidth = width
					end
				else
					if (not item.finishedAt) then
						item.finishedAt = time()
					end
					local width = entry:SetWinnerText(prefix)
					if (width and width > maxWidth) then
						maxWidth = width
					end
				end
			end
			
			if (item.playerRoll) then
				if (item.playerRoll == lootlog.LootWindow.PASSED) then
					entry:SetPlayerText(string.format(PASSED_PATTERN, YOU))
				else
					local width = entry:SetPlayerText(string.format(_GetPattern(item.playerRoll), YOU, (item.playerDice or "")))
					if (width and width > maxWidth) then
						maxWidth = width
					end
				end
			else
				entry:SetPlayerText("")
			end
			
			if (item.winnerRoll) then
				if (item.winnerRoll == lootlog.LootWindow.PASSED) then
					entry:SetWinnerText(string.format(PASSED_PATTERN, ALL))
				else
					local playerName
					if (item.winnerName == self.playerName) then
						playerName = YOU
					else
						playerName = _GetName(self, item.winnerName)
					end
					local width = entry:SetWinnerText(string.format(_GetPattern(item.winnerRoll), playerName, (item.winnerDice or "")))
					if (width and width > maxWidth) then
						maxWidth = width
					end
					if (item.winnerName == self.playerName and (item.winnerRoll == NEED or not self.db.profile.wonNeedOnly)) then
						entry:SetBackgroundColor(unpack(self.db.profile.wonBackgroundColor))
					elseif (item.winnerName ~= self.playerName and (item.playerRoll == NEED or not self.db.profile.lostNeedOnly)) then
						entry:SetBackgroundColor(unpack(self.db.profile.lostBackgroundColor))
					end
				end
			end
		end
	end
	
	local w = WIDTH
	if (maxWidth > 0) then
		w = max(WIDTH, WIDTH_OFFSET + maxWidth)
	end
	for i=1,NUM_ENTRIES do
		local entry = self.entries[i]
		entry:SetWidth(w)
	end
	self.frame:SetScale(self.db.profile.scale)
end

local function _CleanupItems(self)
	local n = getn(self.items)
	local now = time()
	for i=n,1,-1 do
		local item = self.items[i]
		if (not self.db.profile.testMode and item.finishedAt and (now - item.finishedAt >= self.db.profile.fadeoutTime)) then
			tremove(self.items, i)
		end
	end
end

--[[ 
	Creates a new instance.
--]]
function lootlog.LootWindow:new()
	local instance = setmetatable({}, self.metatable)
	instance.db = lootlog.LootLog.db
	instance.items = {}
	instance.updateDelta = 0
	instance.playerName = GetUnitName("player")
	instance.classCache = {}
	instance.englishClassCache = {}
	
	_InitFrame(instance)
	_RestoreWindow(instance)
	
	if (getn(instance.items) > 0) then
		instance:Update(10)
	end
	
	return instance
end

function lootlog.LootWindow.prototype:Update(delta)
	if (self.isUpdating or self.ignoreUpdate) then
		return
	end
	self.isUpdating = true
	
	self.updateDelta = self.updateDelta + (delta or 10)
	if (self.updateDelta >= UPDATE_DELAY) then
		self.frame:SetMovable(not self.db.profile.locked)
		self.updateDelta = 0
		_CleanupItems(self)
		if (getn(self.items) == 0) then
			self.frame:Hide()
		else
			self.frame:Show()
			_UpdateItems(self)
		end
	end
	
	self.isUpdating = nil
end

function lootlog.LootWindow.prototype:AddRoll(rollId, name, itemLink, quality)
	log:Debug("AddRoll rollId [%s] name [%s] itemLink [%s]", rollId, name, itemLink)
	if (getn(self.items) >= NUM_ENTRIES) then
		tremove(self.items, 1)
	end
	local item = {rollId = rollId, name = name, itemLink = itemLink, quality = quality, rolls = {}}
	tinsert(self.items, item)
	self:Update()
	log:Debug("AddRoll exit items [%s]", getn(self.items))
end

function lootlog.LootWindow.prototype:SetRoll(roll, dice, itemName, playerName)
	log:Debug("SetRoll roll [%s] dice [%s] itemName [%s] playerName [%s]", roll, dice, itemName, playerName)
	local n = getn(self.items)
	if (n <= 0) then
		log:Debug("Exit without items")
		return
	end
	
	for i=1,n do
		local item = self.items[i]
		log:Debug("check item [%s] roll [%s]", item.name, item.roll)
		if (itemName == item.name) then
			log:Debug("item match")
			if (playerName == self.playerName) then
				log:Debug("player match")
				if (not item.playerRoll or not item.playerDice) then
					log:Debug("set player")
					item.playerRoll = roll
					item.playerDice = dice
					item.rolls[playerName] = {roll = roll, dice = dice}
					break
				end
			elseif (not item.rolls[playerName] or (dice and not item.rolls[playerName].dice)) then
				log:Debug("update rolls")
				item.rolls[playerName] = {roll = roll, dice = dice}
				_DetermineIntermediate(self, item)
				break
			end
		end
	end
end

function lootlog.LootWindow.prototype:SetWinner(itemName, playerName)
	log:Debug("SetWinner itemName [%s] playerName [%s]", itemName, playerName)
	local n = getn(self.items)
	if (n <= 0) then
		log:Debug("Exit without items")
		return
	end

	for i=1,n do
		local item = self.items[i]
		if (itemName == item.name and not item.winnerRoll) then
			log:Debug("item match")
			if (not playerName) then
				log:Debug("all have passed")
				-- all have passed
				item.winnerRoll = lootlog.LootWindow.PASSED
			else
				local info = item.rolls[playerName]
				if (info) then
					log:Debug("set roll and dice")
					item.winnerRoll = info.roll
					item.winnerDice = info.dice
					item.winnerName = playerName
					break
				else
					log:Debug("no info found")
				end
			end
		end
	end
end

function lootlog.LootWindow.prototype:TestModeUpdate()
	wipe(self.items)
	if (self.db.profile.testMode) then
		if (self.db.profile.minQuality <= 4) then
			tinsert(self.items, {winnerRoll = DISENCHANT, winnerName = self.playerName, winnerDice = 17, itemLink = "item:73740:0:0:0:0:0:0:0:0:0", quality = 4, playerRoll = DISENCHANT, playerDice = 17})
		end
		if (self.db.profile.minQuality <= 2) then
			tinsert(self.items, {testMsec = 66000, itemLink = "item:8216:0:0:0:0:0:0:0:0:0", playerRoll = lootlog.LootWindow.NEED, quality = 2, playerDice = 110, needName = "Lethan", numNeed = 1})
		end
		if (self.db.profile.minQuality <= 3) then
			tinsert(self.items, {testMsec = 66000, itemLink = "item:35590:0:0:0:0:0:0:0:0:0", quality = 3, playerRoll = lootlog.LootWindow.GREED, playerDice = 33, needName = "Borkan", numNeed = 3})
		end
		if (self.db.profile.minQuality <= 5) then
			tinsert(self.items, {winnerRoll = NEED, winnerName = self.playerName, winnerDice = 1, itemLink = "item:77949:0:0:0:0:0:0:0:0:0", quality = 5, playerRoll = NEED, playerDice = 1})
		end
		if (self.db.profile.minQuality <= 4) then
			tinsert(self.items, {winnerRoll = NEED, winnerName = "Lethan", winnerDice = 100, itemLink = "item:78365:0:0:0:0:0:0:0:0:0", quality = 4, playerRoll = NEED, playerDice = 99})
		end
	end
	_InitClassCache(self)
	self:Update(10)
end

function lootlog.LootWindow.prototype:ProfileUpdate()
	_RestoreWindow(self)
	self:TestModeUpdate()
end
